package modules.gameCommunication;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;

import modules.configuration.GameConf;
import objects.Account;
import objects.Character;
import objects.Item;
import objects.Map;

import common.GameServer;
import common.LoadingData;
import common.PacketFormatter;

public class DataCenter implements Runnable {

    private Socket _s;
    private BufferedReader _in;
    private PrintWriter _out;
    private boolean _running = false;
    private boolean _isAuth = false;
    private String _ip = "";
    private Thread _t;
    private PacketParser pckParse;
    private PacketData pckData;
    private PacketFormatter pckFormat;
    private Account account;

    public DataCenter(Socket s) {
        _s = s;
        try {
            _in = new BufferedReader(new InputStreamReader(_s.getInputStream()));
            _out = new PrintWriter(s.getOutputStream());
        } catch (Exception e) {
            return;
        };
        _ip = s.getInetAddress().getHostAddress();

        pckParse = new PacketParser(this);
        pckData = new PacketData(this);
        pckFormat = new PacketFormatter(this);

        GameServer.writeLine("New Connection from " + _ip);

        _t = new Thread(this);
        _t.setDaemon(true);
        _t.start();
    }

    @Override
    public void run() {
        _running = true;
        try {
            String packet = "";
            char charCur[] = new char[1];

            //Send HelloGame Packet
            sendPacket(pckFormat.getHelloGamePacket());

            //Start to read incoming Packets
            while (_in.read(charCur, 0, 1) != -1 && GameServerThread.isRunning() && _running) {
                if (charCur[0] != '\u0000' && charCur[0] != '\n' && charCur[0] != '\r') {
                    packet += charCur[0];
                } else if (packet != "") {
                    if (GameConf._debug) {
                        GameServer.writeLine("Recv << " + packet);
                    }
                    pckParse.parsePacket(packet);
                    packet = "";
                }
            }
        } catch (IOException e) {
            //todo
        } finally {
            if (pckFormat.getCharacter() != null && pckFormat.getCharacter().isOnline()) {
                pckFormat.getCharacter().setOnline(false);
                pckFormat.getCharacter().saveChar();
                pckFormat.removeFormCharactersList();
                pckFormat.getCharacter().deleteFromMap();
                pckFormat.updateGMDatas("-", pckFormat.getCharacter());
            }

            _out.close();
            try {
                _in.close();
            } catch (Exception e) {
            }
            try {
                _s.close();
            } catch (Exception e) {
            }
            GameServer.writeLine("Connection from " + _ip + " closed");
        }
    }

    public void sendPacket(String packet) {
        if (!_s.isConnected() || _s.isClosed()) {
            return;
        }

        try {
            _out.write(packet + (char) 0x00);
            _out.flush();
            if (GameConf._debug) {
                GameServer.writeLine("Send >> " + packet);
            }
        } catch (Exception e) {
            _running = false;
        }
    }

    public void sendTargetedPacket(PrintWriter pOut, String packet) {
        if (!_s.isConnected() || _s.isClosed()) {
            return;
        }

        try {
            pOut.write(packet + (char) 0x00);
            pOut.flush();
            if (GameConf._debug) {
                GameServer.writeLine("Send To Target >> " + packet);
            }
        } catch (Exception e) {
            _running = false;
        }
    }

    public void sendPacketToAllClients(String packet) {
        if (!_s.isConnected() || _s.isClosed()) {
            return;
        }

        try {
            Iterator<DataCenter> iterator = GameServerThread.getClients().iterator();
            while (iterator.hasNext()) {
                DataCenter element = iterator.next();

                element.getOut().write(packet + (char) 0x00);
                element.getOut().flush();
            }
            if (GameConf._debug) {
                GameServer.writeLine("Send Broadcast >> " + packet);
            }
        } catch (Exception e) {
            _running = false;
        }
    }

    public void sendPacketToMap(int mapID, String packet) {
        if (!_s.isConnected() || _s.isClosed()) {
            return;
        }

        try {
            Hashtable<Integer, Map> getMapDatas = LoadingData.getMapData();
            Map mapDatas = getMapDatas.get(mapID);

            // Get all characters on the map
            Hashtable<Long, Character> onMapCharacters = mapDatas.getCharacterOnMap();

            Enumeration<Long> enumCharacters = onMapCharacters.keys();

            while (enumCharacters.hasMoreElements()) {
                long key = (Long) enumCharacters.nextElement(); // Next Character
                Character onMapCharacterDatas = onMapCharacters.get(key);

                this.sendTargetedPacket(onMapCharacterDatas.getOut(), packet);
            }
            if (GameConf._debug) {
                GameServer.writeLine("Send To Map >> " + packet);
            }
        } catch (Exception e) {
            _running = false;
        }
    }

    public void sendPacketToAdmins(String packet) {
        if (!_s.isConnected() || _s.isClosed()) {
            return;
        }

        try {
            Iterator<DataCenter> iterator = GameServerThread.getClients().iterator();
            while (iterator.hasNext()) {
                DataCenter element = iterator.next();

                if (element.getAccount().getGmLevel() > 0) {
                    element.getOut().write(packet + (char) 0x00);
                    element.getOut().flush();
                }
            }
            if (GameConf._debug) {
                GameServer.writeLine("Send To Admin >> " + packet);
            }
        } catch (Exception e) {
            _running = false;
        }
    }

    public void setIsAuth(boolean isAuth) {
        _isAuth = isAuth;
    }

    public boolean getIsAuth() {
        return _isAuth;
    }

    public void setRunning(boolean running) {
        _running = running;
    }

    public boolean getRunning() {
        return _running;
    }

    public String getIP() {
        return _ip;
    }

    public void setIP(String ip) {
        _ip = ip;
    }

    public PacketParser getPckParse() {
        return pckParse;
    }

    public void setPckParse(PacketParser pckParse) {
        this.pckParse = pckParse;
    }

    public PacketData getPckData() {
        return pckData;
    }

    public void setPckData(PacketData pckData) {
        this.pckData = pckData;
    }

    public PacketFormatter getPckFormat() {
        return pckFormat;
    }

    public void setPckFormat(PacketFormatter pckFormat) {
        this.pckFormat = pckFormat;
    }

    public PrintWriter getOut() {
        return _out;
    }

    public void setOut(PrintWriter _out) {
        this._out = _out;
    }

    public Account getAccount() {
        return account;
    }

    public void setAccount(Account account) {
        this.account = account;
    }

    public void parseCommands(String packet) {
        if (account.getGmLevel() < 1) {
            GameServer.writeLine(Byte.toString(account.getGmLevel()));
            sendPacket("Im13");
            return;
        }

        String msg = packet.substring(2);
        String[] infos = msg.split(" ");
        if (infos.length == 0) {
            return;
        }
        String command = infos[0];
        String[] args = msg.split(" ");

        if (command.equalsIgnoreCase("ALL")) //
        {
            if (args.length > 1) {
                String param = packet.substring(6);
                sendPacketToAllClients("cs<font color='#FF0000'>[ALL] " + param + "</font>");
            } else {
                sendPacket(getArgumentsUndefined());
            }

            return;
        } else if (command.equalsIgnoreCase("MAP")) //
        {
            if (args.length > 1) {
                String param = packet.substring(6);
                sendPacketToMap(getPckFormat().getCharacter().getMapID(), "cs<font color='#FF0000'>[MAP] " + param + "</font>");
            } else {
                sendPacket(getArgumentsUndefined());
            }

            return;
        } else if (command.equalsIgnoreCase("goto")) // goto mapID cell
        {
            if (args.length > 1) {
                getPckFormat().changeMap(getPckFormat().getCharacter().getMapID(), Short.parseShort(infos[1]), args.length > 2 ? Short.parseShort(args[2]) : 1);
            } else {
                sendPacket(getArgumentsUndefined());
            }

            return;
        } else if (command.equalsIgnoreCase("add")) // add itemID
        {
            if (args.length > 1) {
                Item item = getPckFormat().getCharacter().addItem(Integer.parseInt(args[1]), (args.length > 2) ? Integer.parseInt(args[2]) : 1);
                if (item != null) {
                    sendPacket("OS+" + item.getGuid() + "|" + Item.formatedEffectsASK(item.getEffects()) + "|" + 0); // FIXME : id effect?
                } else {
                    sendPacket("Unable to add item " + args[1]);
                }
                return;
            } else {
                sendPacket(getArgumentsUndefined());
                return;
            }
        } else if (command.equalsIgnoreCase("size")) //change size
        {
            try {
                int a = Integer.parseInt(args[1]);
                if (a > 0 && a < 1000) {
                	getPckFormat().getCharacter().setSize(a);
                    getPckFormat().updateGMDatas("~", getPckFormat().getCharacter());
                }
                
                return;
            } catch (Exception e) {
                sendPacket(getArgumentsUndefined());
            };
        } else if (command.equalsIgnoreCase("morph")) //change Gfxid
        {
            try {
                int a = Integer.parseInt(args[1]);
                if (a > 0 && a < 10000) {
                	getPckFormat().getCharacter().setGfxid(a);
                    getPckFormat().updateGMDatas("~", getPckFormat().getCharacter());
                }
                
                return;
            } catch (Exception e) {
                sendPacket(getArgumentsUndefined());
            };
        } else if (command.equalsIgnoreCase("unmorph")) //reset Gfxid
        {
        	getPckFormat().getCharacter().setGfxid(getPckFormat().getCharacter().getRace()*10 + getPckFormat().getCharacter().getSex());
            getPckFormat().updateGMDatas("~", getPckFormat().getCharacter());
            
            return;
        }
    }

    public String getArgumentsUndefined() {
        return "BAT2incorect number of arguments !";
    }
}
